package Engine.Scene;

import Engine.Character.BattleCharacter;
import Engine.Character.EnemyCharacter;
import Engine.Character.OverworldCharacter;
import Engine.Game.GameAdmin;
import Engine.Game.GameDisplay;
import java.io.IOException;
import java.util.Random;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

/**
 * Handles Battle events. On start, it generates a random amount of steps needed to trigger a battle. Once triggered, it generates a random amount
 * of enemies (1-3) with a given level, calculated using the level of the main player. The class handles turns, attack events (physical/magical),
 * item events, battle continuity and requests animations to its GameCanvas and damage calculation to the corresponding character.
 * If the battle ends succesfully, it calculates the corresponding experience and money gained, passed to the main player which then handles if
 * it should level up. Finally it calculates the next random amount of steps need to trigger the next battle.
 * If the battle does not ends succesfully, it triggers a Game Over screen and asks if the player wants to continue.
 *
 * @author Gerardo Galíndez
 */
public class BattleManager {

    //--------------------------------------------------
    //Object references---------------------------------
    private GameAdmin admin;
    private GameDisplay screen;
    private Random randomGenerator;
    private OverworldCharacter currentPlayer;
    private Graphics g;
    private Sprite healEffect;
    private Sprite restoreEffect;
    private Sprite fireEffect;
    private Sprite iceEffect;
    private int enemyQuantity;
    private int damage;
    private boolean isAttacking;
    private boolean noMoreContinue;
    private boolean movingForward;
    //Active characters---------------------------------
    private BattleCharacter mainPlayer;
    //Battle status-------------------------------------
    private int status;
    private int currentCrossSelection;
    private int escapeCounter;
    //Battle items--------------------------------------
    private int healthPotions;
    private int magicPotions;
    private int promoPotions;
    /**
     * BattleManager integer index representing that the BattleManager is not working.
     */
    public static final int IDLE = 0;
    /**
     * BattleManager integer index representing that the BattleManager is ready to receive orders.
     */
    public static final int ACTIVE_PLAYER_TURN = 1;
    /**
     * BattleManager integer index representing that the BattleManager is deciding the enemies' actions.
     */
    public static final int ACTIVE_ENEMY_TURN = 2;
    /**
     * BattleManager integer index representing that the BattleManager is painting the animation.
     */
    public static final int ACTIVE_BATTLE = 3;
    /**
     * BattleManager integer index representing that the BattleManager has finished a battle.
     */
    public static final int FINISHED = 4;
    //Cross menu reference constants--------------------
    /**
     * BattleManager integer index representing the Cross-selection menu's up.
     */
    public static final int CROSS_UP = 0;
    /**
     * BattleManager integer index representing the Cross-selection menu's down.
     */
    public static final int CROSS_DOWN = 1;
    /**
     * BattleManager integer index representing the Cross-selection menu's left.
     */
    public static final int CROSS_LEFT = 2;
    /**
     * BattleManager integer index representing the Cross-selection menu's right.
     */
    public static final int CROSS_RIGHT = 3;
    /**
     * BattleManager integer index representing the Cross-selection menu's "Main" screen.
     */
    public static final int CROSS_MAIN = 0;
    /**
     * BattleManager integer index representing the Cross-selection menu's "Item" screen.
     */
    public static final int CROSS_ITEM = 1;
    /**
     * BattleManager integer index representing the Cross-selection menu's "Special" screen.
     */
    public static final int CROSS_SPECIAL = 2;
    /**
     * BattleManager integer index representing the Cross-selection menu's "Magic" screen.
     */
    public static final int CROSS_MAGIC = 3;
    /**
     * BattleManager integer index representing the Cross-selection menu's "Target" screen.
     */
    public static final int CROSS_TARGET = 4;
    private int currentTarget;
    private int currentAttacker;
    private int originalPlayerX;
    private int originalEnemyX;
    private final int[] supportSequence = {0,1,1,1,2,2,2,2,1,1,1,0};
    private final int[] offenseSequence = {0,1,1,1,2,2,2,3,3,3,2,2,2,1,1,1,0};
    private final int ENEMY1 = 0;
    private final int ENEMY2 = 1;
    private final int ENEMY3 = 2;
    private final int PLAYER = 3;
    //--------------------------------------------------
    private EnemyCharacter[] enemyArray;
    private final int MAX_STEPS = 55; //Value: 55
    private final int MIN_STEPS = 40; //Value: 40
    private char actionPerformed;
    private static final char PHYSICAL_ATTACK = 'a';
    private static final char PROMO_POTION = 'b';
    private static final char HEALTH_POTION = 'c';
    private static final char ETHER_POTION = 'd';
    private static final char FIRE_MAGIC = 'e';
    private static final char ICE_MAGIC = 'f';
    private static final char HEAL_MAGIC = 'g';

    /**
     * Creates a new BattleManager with its references to the administrator and display. The constructor also initializes a set of random
     * routines to generate the number of steps taken to trigger the next battle.
     * @param parentAdmin Owner of the current BattleManager.
     * @param currentPlayer User's player.
     * @param currentScreen Current GameCanvas.
     */
    public BattleManager(GameAdmin parentAdmin, OverworldCharacter currentPlayer, GameDisplay currentScreen) {
        this.admin = parentAdmin;
        this.screen = currentScreen;
        this.status = IDLE;
        this.g = currentScreen.getG();
        this.mainPlayer = new BattleCharacter(currentPlayer.getCharacterCode());
        try {
            this.healEffect = new Sprite(Image.createImage("/Images/Battle/Heal.png"), 51, 34);
            this.healEffect.setFrameSequence(supportSequence);
            this.restoreEffect = new Sprite(Image.createImage("/Images/Battle/MP.png"), 51, 34);
            this.restoreEffect.setFrameSequence(supportSequence);
            this.fireEffect = new Sprite(Image.createImage("/Images/Battle/Fire.png"), 48, 32);
            this.fireEffect.setFrameSequence(offenseSequence);
            this.iceEffect = new Sprite(Image.createImage("/Images/Battle/Ice.png"), 40, 40);
            this.iceEffect.setFrameSequence(offenseSequence);
        } catch (IOException ioex) {
            ioex.printStackTrace();
        }
        randomGenerator = new Random();
        this.generateNextSteps();
        this.screen.setManager(this);
        this.currentPlayer = currentPlayer;
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<--------------------------------------------------------Random generators----------------------------------------------------------->>

    //Creates a randon double number
    private double generateRandomNumber() {
        return randomGenerator.nextDouble();
    }

    //Returns the variable noMoreContinue
    public boolean getNoMoreContinue(){
        return this.noMoreContinue;
    }

    /**
     * Generates a random number between the constants MAX_STEPS and MIN_STEPS.
     */
    public void generateNextSteps() {
        double generatedNumber = (generateRandomNumber() * MAX_STEPS);
        while (generatedNumber < MIN_STEPS) {
            generatedNumber += generateRandomNumber() * MIN_STEPS / 2;
        }
        admin.setNextBattle((int) generatedNumber);
    }

    //Sets the number of enemies according to the player level
    private void determineEnemyQuantity() {
        enemyQuantity = 1;
        double quantityProbability = generateRandomNumber();
        if (mainPlayer.getCurrentLevel() < 5) {
            //Probability of lower levels
            if (quantityProbability > 0.50) {
                enemyQuantity++;
            }
            if (quantityProbability > 0.85) {
                enemyQuantity++;
            }
        } else {
            //Probability of higher levels
            if (quantityProbability > 0.20) {
                enemyQuantity++;
            }
            if (quantityProbability > 0.60) {
                enemyQuantity++;
            }
        }
        System.out.println("Enemy Array created succesfully with " + enemyQuantity + " enemies!");
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<--------------------------------------------------------Battle flow control--------------------------------------------------------->>

    /**
     * Initializes a new battle
     */
    public void startBattle() {
        this.currentCrossSelection = BattleManager.CROSS_MAIN;
        this.currentTarget = ENEMY1;
        double battleMusicCode = this.generateRandomNumber() * 3;
        admin.startBattle((int) battleMusicCode);
        this.initializeResources();
        this.status = ACTIVE_PLAYER_TURN;
        this.originalPlayerX = mainPlayer.getX();
        this.originalEnemyX = enemyArray[0].getX();
        while (screen.getKeyStates() != 0) {
        }
    }

    /**
     * Is called when the battle has finished. Sets the enemy array to null and calls gameAdmin to
     * change the screen to overworld
     */
    public void finishBattle() {
        this.enemyArray = null;
        this.generateNextSteps();
        admin.finishBattle();
    }

    /**
     * 1. Status is ACTIVE_PLAYER_TURN. Check if player(s) are alive. If not, change the status to FINISHED and call a Game Over.
     * 2. Handle the corresponding action and calculate damage.
     * 3. Check if there are any players left. If there are, repeat step 2.
     * 4. Change the status to ACTIVE_ENEMY_TURN.
     * 5. Check if enemy(ies) are alive. if not, If not, change the status to FINISHED and call a Battle Win.
     * 6. Decide the action taken and calculate damage.
     * 7. Change the status to ACTIVE_BATTLE
     * 8. Paint the corresponding animations.
     * 9. Cycle until a FINISHED is signaled.
     */
    public void battleCycle() {
        switch (status) {
            case ACTIVE_PLAYER_TURN:
                if (currentPlayer.isAlive()) {
                    this.screen.handleBattleKeyboard();
                } else {
                    if (noMoreContinue) {
                        this.status = FINISHED;
                        this.screen.setGameOver(true);
                    } else {
                        noMoreContinue = !noMoreContinue;
                        currentPlayer.setCurrentHealthPoints(1);
                        this.status = FINISHED;
                    }
                }
                break;
            case ACTIVE_ENEMY_TURN:
                System.out.println("Player HP: " + currentPlayer.getCurrentHealthPoints());
                System.out.println("Enemy HP: " + enemyArray[0].getCurrentHealthPoints());
                //Check if enemies are alive
                if (testEnemiesAlive()) {
                    System.out.println("Enemies are alive.");
                    enemiesPerformAttack();
                } else {
                    this.status = FINISHED;
                }
                break;
            case ACTIVE_BATTLE:
                //Paint
                if ( isAttacking ) {
                    this.paint();
                }
                break;
            case FINISHED:
                //Calculate exp and money
                /*if (!this.enemyArray[0].isAlive()) {
                    this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[0].getEnemyCode()));
                    if (noMoreContinue) {
                        noMoreContinue = !noMoreContinue;
                    }
                }
                 *
                 */
                if ( !this.testEnemiesAlive() ) {
                    calculateExperience();
                    if (noMoreContinue) {
                        noMoreContinue = !noMoreContinue;
                    }
                }
                this.screen.finishBattle();
                break;
            default:
                break;
        }
    }

    //Calculates the experience gained in a battle
    private void calculateExperience() {
        switch (enemyQuantity) {
            case 1:
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY1].getEnemyCode()));
                break;
            case 2:
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY1].getEnemyCode()));
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY2].getEnemyCode()));
                break;
            case 3:
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY1].getEnemyCode()));
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY2].getEnemyCode()));
                this.currentPlayer.addExperiencePoints(EnemyCharacter.getExperiencePoints(enemyArray[ENEMY3].getEnemyCode()));
                break;
            default:
                System.out.println("Bad enemy quantity.");
        }
    }

    /**
     * Accesor method to check the status
     * @return current status of battle
     */
    public int checkBattleStatus() {
        return this.status;
    }

    //Checks if all enemies are alive
    private boolean testEnemiesAlive() {
        switch (enemyQuantity) {
            case 1:
                return enemyArray[0].isAlive();
            case 2:
                return enemyArray[0].isAlive() || enemyArray[1].isAlive();
            case 3:
                return enemyArray[0].isAlive() || enemyArray[1].isAlive() || enemyArray[2].isAlive();
            default:
                System.out.println("Bad alive check.");
                return false;
        }
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<------------------------------------------------------Character initialization------------------------------------------------------>>

    //The resources, including players and enemies are inicialized
    private void initializeResources() {
        initializePlayers();
        initializeEnemies();
        initializeItems();
    }

    //The players are inicialized, using the method setPlayersPosition
    private void initializePlayers() {
        setPlayersPosition();
    }

    //The players are inicialized, using the methods determineEnemyQuantity, createEnemies and setEnemiesPosition
    private void initializeEnemies() {
        determineEnemyQuantity();
        createEnemies();
        setEnemiesPosition();
    }

    //The array of enemies is created, according to the enemy quantity.
    private void createEnemies() {
        enemyArray = new EnemyCharacter[enemyQuantity];
        int counter;
        int enemyIndex;
        char enemyCode;
        switch (enemyQuantity) {
            case 1:
                enemyIndex = (int) (this.generateRandomNumber() * EnemyCharacter.ONE_ENEMY_TYPES_QUANTITY);
                enemyCode = EnemyCharacter.oneEnemyTypes[enemyIndex];
                for (counter = 0; counter < enemyArray.length; counter++) {
                    enemyArray[counter] = new EnemyCharacter(enemyCode);
                }
                break;
            case 2:
                enemyIndex = (int) (this.generateRandomNumber() * EnemyCharacter.TWO_ENEMY_TYPES_QUANTITY);
                enemyCode = EnemyCharacter.twoEnemiesTypes[enemyIndex];
                for (counter = 0; counter < enemyArray.length; counter++) {
                    enemyArray[counter] = new EnemyCharacter(enemyCode);
                }
                break;
            case 3:
                enemyIndex = (int) (this.generateRandomNumber() * EnemyCharacter.THREE_ENEMY_TYPES_QUANTITY);
                enemyCode = EnemyCharacter.threeEnemiesTypes[enemyIndex];
                for (counter = 0; counter < enemyArray.length; counter++) {
                    enemyArray[counter] = new EnemyCharacter(enemyCode);
                }
                break;
            default:
                System.out.println("Bad enemy quantity");
        }
        
    }

    //Initialize the battle items and the escape counter
    private void initializeItems() {
        this.escapeCounter = 0;
        this.healthPotions = 3;
        this.magicPotions = 3;
        this.promoPotions = 1;
    }

    //Sets the player's position in the screen.
    private void setPlayersPosition() {
        int horizontalPosition = screen.getWidth() - this.mainPlayer.getWidth();
        int verticalPosition = screen.getSCREEN_Y_THIRD_QUARTER() - this.mainPlayer.getHeight()/2;
        mainPlayer.setPosition(horizontalPosition, verticalPosition);
    }

    //Sets the enemies' position in the screen.
    private void setEnemiesPosition() {
        switch (enemyQuantity) {
            case 1:
                enemyArray[ENEMY1].setPosition(10, screen.getSCREEN_Y_THIRD_QUARTER()-enemyArray[ENEMY1].getHeight()/2);
                break;
            case 2:
                enemyArray[ENEMY1].setPosition(10, screen.getSCREEN_Y_THIRD_QUARTER()-enemyArray[ENEMY1].getHeight());
                enemyArray[ENEMY2].setPosition(10, screen.getSCREEN_Y_THIRD_QUARTER());
                break;
            case 3:
                enemyArray[ENEMY1].setPosition(10, screen.getSCREEN_Y_THIRD_QUARTER()-enemyArray[ENEMY1].getHeight());
                enemyArray[ENEMY2].setPosition(10, screen.getSCREEN_Y_THIRD_QUARTER());
                enemyArray[ENEMY3].setPosition(enemyArray[ENEMY3].getWidth(), screen.getSCREEN_Y_THIRD_QUARTER()-enemyArray[ENEMY1].getHeight()/2);

                break;
        }
    }

    /**
     * Return the x coordenate of the enemy to be painted
     * @param enemyIndex number of enemies at battle
     * @return the x coordenate of the enemy to paint
     */
    public int determineEnemyXAnchor(int enemyIndex) {
        switch (enemyQuantity) {
            case 1:
                return 10;
            case 2:
                return 10;
            case 3:
                return enemyArray[ENEMY3].getWidth();
            default:
                return 0;
        }
    }

    /**
     * Return the y coordenate of the enemy to be painted
     * @param enemyIndex number of enemies at battle
     * @param enemyHeight the height of the enemy
     * @return the y coordenate of the enemy to paint
     */
    public int determineEnemyYAnchor(int enemyIndex,  int enemyHeight) {
        switch(enemyIndex) {
            case 1:
                return screen.getSCREEN_Y_THIRD_QUARTER() - (enemyHeight / 2);
            case 2:
                return screen.getSCREEN_Y_THIRD_QUARTER() - enemyHeight;
            case 3:
                return 0;
            default:
                System.out.println("Bad Y anchor.");
                return 0;
        }
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<----------------------------------------------------------Graphic methods----------------------------------------------------------->>

    /**
     * Calls the battle objects to be painted in its position
     * @param g graphic object to paint the canvas
     */
    public void paint() {
        if (status == ACTIVE_BATTLE) {
            switch (currentAttacker) {
                case PLAYER:
                    switch ( this.actionPerformed ) {
                        case BattleManager.PHYSICAL_ATTACK:
                            if (mainPlayer.getX() > enemyArray[0].getX() && movingForward) {
                                mainPlayer.moveForward();
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else if (mainPlayer.getX() == enemyArray[0].getX()) {
                                this.movingForward = false;
                                mainPlayer.attack();
                                mainPlayer.paint(g);
                                mainPlayer.retreat();
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else if (mainPlayer.getX() < originalPlayerX) {
                                mainPlayer.retreat();
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.movingForward = true;
                                isAttacking = false;
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.FIRE_MAGIC:
                            this.fireEffect.setVisible(true);
                            this.fireEffect.setPosition(enemyArray[0].getX(), enemyArray[0].getY());
                            if ( this.fireEffect.getFrame() < this.fireEffect.getFrameSequenceLength()-1) {
                                this.fireEffect.nextFrame();
                                this.fireEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.fireEffect.setVisible(false);
                                isAttacking = false;
                                this.fireEffect.setFrame(0);
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.ICE_MAGIC:
                            this.iceEffect.setVisible(true);
                            this.iceEffect.setPosition(enemyArray[0].getX(), enemyArray[0].getY());
                            if ( this.iceEffect.getFrame() < this.iceEffect.getFrameSequenceLength()-1) {
                                this.iceEffect.nextFrame();
                                this.iceEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.iceEffect.setVisible(false);
                                isAttacking = false;
                                this.iceEffect.setFrame(0);
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.HEALTH_POTION:
                            this.healEffect.setVisible(true);
                            this.healEffect.setPosition(this.originalEnemyX, currentPlayer.getY());
                            if ( this.healEffect.getFrame() < this.healEffect.getFrameSequenceLength()-1) {
                                this.healEffect.nextFrame();
                                this.healEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.healEffect.setVisible(false);
                                this.healEffect.setFrame(0);
                                isAttacking = false;
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.ETHER_POTION:
                            this.restoreEffect.setVisible(true);
                            this.restoreEffect.setPosition(this.originalEnemyX, currentPlayer.getY());
                            if ( this.restoreEffect.getFrame() < this.restoreEffect.getFrameSequenceLength()-1) {
                                this.restoreEffect.nextFrame();
                                this.restoreEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.restoreEffect.setVisible(false);
                                this.restoreEffect.setFrame(0);
                                isAttacking = false;
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.HEAL_MAGIC:
                            this.healEffect.setVisible(true);
                            this.healEffect.setPosition(this.originalEnemyX, currentPlayer.getY());
                            if ( this.healEffect.getFrame() < this.healEffect.getFrameSequenceLength()-1) {
                                this.healEffect.nextFrame();
                                this.healEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.healEffect.setVisible(false);
                                this.healEffect.setFrame(0);
                                isAttacking = false;
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                        case BattleManager.PROMO_POTION:
                            this.healEffect.setVisible(true);
                            this.healEffect.setPosition(this.originalEnemyX, currentPlayer.getY());
                            if ( this.healEffect.getFrame() < this.healEffect.getFrameSequenceLength()-1) {
                                this.healEffect.nextFrame();
                                this.healEffect.paint(g);
                                mainPlayer.paint(g);
                                switch (enemyQuantity) {
                                    case 1:
                                        enemyArray[0].paint(g);
                                        break;
                                    case 2:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        break;
                                    case 3:
                                        enemyArray[0].paint(g);
                                        enemyArray[1].paint(g);
                                        enemyArray[2].paint(g);
                                        break;
                                }
                            } else {
                                this.healEffect.setVisible(false);
                                this.healEffect.setFrame(0);
                                isAttacking = false;
                                currentAttacker = ENEMY1;
                                status = ACTIVE_ENEMY_TURN;
                            }
                            break;
                    }
                    
                    break;
                case ENEMY1:
                    if ( enemyArray[0].isVisible() ) {
                        if (enemyArray[0].getX() < mainPlayer.getX() && movingForward) {
                            enemyArray[0].moveForward();
                            enemyArray[0].paint(g);
                            mainPlayer.paint(g);
                        } else if ( enemyArray[0].getX() == mainPlayer.getX()) {
                            this.movingForward = false;
                            enemyArray[0].attack();
                            enemyArray[0].paint(g);
                            enemyArray[0].retreat();
                            mainPlayer.paint(g);
                        } else if (enemyArray[0].getX() > originalEnemyX){
                            enemyArray[0].retreat();
                            enemyArray[0].paint(g);
                            mainPlayer.paint(g);
                        } else {
                            this.movingForward = true;
                            if ( this.enemyQuantity == 2 || this.enemyQuantity == 3 ) {
                                currentAttacker = ENEMY2;
                            } else {
                                isAttacking = false;
                                currentAttacker = PLAYER;
                                status = ACTIVE_PLAYER_TURN;
                                currentCrossSelection = CROSS_MAIN;
                            }
                        }
                    } else {
                        this.movingForward = true;
                            if ( this.enemyQuantity == 2 || this.enemyQuantity == 3 ) {
                                currentAttacker = ENEMY2;
                            } else {
                                isAttacking = false;
                                currentAttacker = PLAYER;
                                status = ACTIVE_PLAYER_TURN;
                                currentCrossSelection = CROSS_MAIN;
                            }
                    }
                    break;
                case ENEMY2:
                    if ( enemyArray[1].isVisible() ) {
                        if (enemyArray[1].getX() < mainPlayer.getX() && movingForward) {
                            enemyArray[1].moveForward();
                            enemyArray[1].paint(g);
                            mainPlayer.paint(g);
                        } else if ( enemyArray[1].getX() == mainPlayer.getX() ) {
                            this.movingForward = false;
                            enemyArray[1].attack();
                            enemyArray[1].paint(g);
                            enemyArray[1].retreat();
                            mainPlayer.paint(g);
                        } else if (enemyArray[1].getX() > originalEnemyX){
                            enemyArray[1].retreat();
                            enemyArray[1].paint(g);
                            mainPlayer.paint(g);
                        } else {
                            this.movingForward = true;
                            if ( this.enemyQuantity == 3 ) {
                                currentAttacker = ENEMY3;
                            } else {
                                isAttacking = false;
                                currentAttacker = PLAYER;
                                status = ACTIVE_PLAYER_TURN;
                                currentCrossSelection = CROSS_MAIN;
                            }
                        }
                    } else {
                        this.movingForward = true;
                            if ( this.enemyQuantity == 3 ) {
                                currentAttacker = ENEMY3;
                            } else {
                                isAttacking = false;
                                currentAttacker = PLAYER;
                                status = ACTIVE_PLAYER_TURN;
                                currentCrossSelection = CROSS_MAIN;
                            }
                    }
                    break;
                case ENEMY3:
                    if ( enemyArray[2].isVisible() ) {
                        if (enemyArray[2].getX() < mainPlayer.getX() && movingForward) {
                            enemyArray[2].moveForward();
                            enemyArray[2].paint(g);
                            mainPlayer.paint(g);
                        } else if ( enemyArray[2].getX() == mainPlayer.getX() ) {
                            this.movingForward = false;
                            enemyArray[2].attack();
                            enemyArray[2].paint(g);
                            enemyArray[2].retreat();
                            mainPlayer.paint(g);
                        } else if (enemyArray[2].getX() > originalEnemyX){
                            enemyArray[2].retreat();
                            enemyArray[2].paint(g);
                            mainPlayer.paint(g);
                        } else {
                            isAttacking = false;
                            currentAttacker = PLAYER;
                            status = ACTIVE_PLAYER_TURN;
                            currentCrossSelection = CROSS_MAIN;
                        }
                    } else {
                        isAttacking = false;
                        currentAttacker = PLAYER;
                        status = ACTIVE_PLAYER_TURN;
                        currentCrossSelection = CROSS_MAIN;
                    }
                    break;
            }
        } else {
            this.movingForward = false;
            mainPlayer.stand();
            mainPlayer.paint(g);
            paintCharacters();
        }
    }

    //Paints the enemies according to the enemy quantity
    private void paintCharacters() {
        switch (enemyQuantity) {
            case 1:
                enemyArray[ENEMY1].stand();
                enemyArray[ENEMY1].paint(g);
                break;
            case 2:
                enemyArray[ENEMY1].stand();
                enemyArray[ENEMY1].paint(g);
                enemyArray[ENEMY2].stand();
                enemyArray[ENEMY2].paint(g);
                break;
            case 3:
                enemyArray[ENEMY1].stand();
                enemyArray[ENEMY1].paint(g);
                enemyArray[ENEMY2].stand();
                enemyArray[ENEMY2].paint(g);
                enemyArray[ENEMY3].stand();
                enemyArray[ENEMY3].paint(g);
                break;
            default:
        }
    }

    /**
     * Displays an arrow above the current target.
     * @param g graphic object to paint the canvas
     * @param target where the arrow will be painted
     */
    public void paintTarget(Image target) {
        switch (currentTarget) {
            case ENEMY1:
                System.out.println("Enemy 1 selected");
                g.drawImage(target, enemyArray[ENEMY1].getX(), enemyArray[ENEMY1].getY(), Graphics.LEFT | Graphics.BOTTOM);
                break;
            case ENEMY2:
                System.out.println("Enemy 2 selected");
                g.drawImage(target, enemyArray[ENEMY2].getX(), enemyArray[ENEMY2].getY(), Graphics.LEFT | Graphics.BOTTOM);
                break;
            case ENEMY3:
                System.out.println("Enemy 3 selected");
                g.drawImage(target, enemyArray[ENEMY3].getX(), enemyArray[ENEMY3].getY(), Graphics.LEFT | Graphics.BOTTOM);
                break;
            case PLAYER:
                g.drawImage(target, mainPlayer.getX() + mainPlayer.getWidth(), mainPlayer.getY(), Graphics.RIGHT | Graphics.BOTTOM);
                break;
        }
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<-----------------------------------------------------------Cross methods------------------------------------------------------------>>

    /**
     * Handles a click event from the keyboard.
     */
    public void handleClick() {
        if (currentCrossSelection == CROSS_TARGET) {
            this.status = BattleManager.ACTIVE_BATTLE;
            this.currentAttacker = PLAYER;
            this.isAttacking = true;
            this.movingForward = true;
            switch (actionPerformed) {
                case PHYSICAL_ATTACK:
                    usePhysicalAttack();
                    break;
                case PROMO_POTION:
                    usePromoPotion();
                    break;
                case HEALTH_POTION:
                    useHealthPotion();
                    break;
                case ETHER_POTION:
                    useEtherPotion();
                    break;
                case FIRE_MAGIC:
                    useFireMagic();
                    break;
                case ICE_MAGIC:
                    useIceMagic();
                    break;
                case HEAL_MAGIC:
                    useHealMagic();
                    break;
            }
        } else {
            backSelection();
        }
    }

    /**
     * Handles a left click event, corresponding to an action according to the cross selection menu.
     */
    public void leftCrossSelection() {
        switch (currentCrossSelection) {
            case CROSS_MAIN:
                actionPerformed = PHYSICAL_ATTACK;
                currentTarget = ENEMY1;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_ITEM:
                actionPerformed = PROMO_POTION;
                currentTarget = PLAYER;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_MAGIC:
                actionPerformed = FIRE_MAGIC;
                currentTarget = ENEMY1;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_TARGET:
                currentTarget = ENEMY1;
                break;
            default:
                System.out.println("Bad cross selection");
        }
    }

    /**
     * Handles a right click event, corresponding to an action according to the cross selection menu.
     */
    public void rightCrossSelection() {
        switch (currentCrossSelection) {
            case CROSS_MAIN:
                escape();
                break;
            case CROSS_ITEM:
                actionPerformed = ETHER_POTION;
                currentTarget = PLAYER;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_MAGIC:
                actionPerformed = HEAL_MAGIC;
                currentTarget = PLAYER;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_TARGET:
                currentTarget = PLAYER;
                break;
            default:
                System.out.println("Bad cross selection");
        }
    }

    /**
     * Handles a up click event, corresponding to an action according to the cross selection menu.
     */
    public void upCrossSelection() {
        switch (currentCrossSelection) {
            case CROSS_MAIN:
                currentCrossSelection = CROSS_ITEM;
                break;
            case CROSS_ITEM:
                actionPerformed = HEALTH_POTION;
                currentTarget = PLAYER;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_MAGIC:
                actionPerformed = ICE_MAGIC;
                currentTarget = ENEMY1;
                currentCrossSelection = CROSS_TARGET;
                break;
            case CROSS_TARGET:
                previousTarget();
                break;
            default:
                System.out.println("Bad cross selection");
        }
    }

    /**
     * Handles a down click event, corresponding to an action according to the cross selection menu.
     */
    public void downCrossSelection() {
        switch (currentCrossSelection) {
            case CROSS_MAIN:
                currentCrossSelection = CROSS_MAGIC;
                break;
            case CROSS_ITEM:
                backSelection();
                break;
            case CROSS_MAGIC:
                backSelection();
                break;
            case CROSS_TARGET:
                nextTarget();
                break;
            default:
                System.out.println("Bad cross selection");
        }
    }

    /**
     * Return to the cross main
     */
    public void backSelection() {
        currentCrossSelection = CROSS_MAIN;
    }

    /**
     * Selects the previous target according to their positions.
     */
    public void previousTarget() {
        switch (enemyQuantity) {
            case 1:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY1;
                        break;
                }
                break;
            case 2:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case ENEMY2:
                        currentTarget = ENEMY1;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY2;
                        break;
                }
                break;
            case 3:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case ENEMY2:
                        currentTarget = ENEMY1;
                        break;
                    case ENEMY3:
                        currentTarget = ENEMY2;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY3;
                        break;
                }
                break;
            default:
                System.out.println("Bad enemy quantity at previousTarget()");
        }
    }

    //Not finished
    /**
     * Selects the next target according to their positions.
     */
    public void nextTarget() {
        switch (enemyQuantity) {
            case 1:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY1;
                        break;
                }
                break;
            case 2:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case ENEMY2:
                        currentTarget = ENEMY1;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY2;
                        break;
                }
                break;
            case 3:
                switch (currentTarget) {
                    case ENEMY1:
                        currentTarget = PLAYER;
                        break;
                    case ENEMY2:
                        currentTarget = ENEMY1;
                        break;
                    case ENEMY3:
                        currentTarget = ENEMY2;
                        break;
                    case PLAYER:
                        currentTarget = ENEMY3;
                        break;
                }
                break;
            default:
                System.out.println("Bad enemy quantity at previousTarget()");
        }
    }

    /**
     * Returns the current cross-selection menu status.
     * @return Cross-selection menu integer index.
     */
    public int getCurrentCrossSelection() {
        return currentCrossSelection;
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
    //<<----------------------------------------------------------Battle Actions------------------------------------------------------------>>

    //Enemy actions
    private void enemiesPerformAttack() {
        switch (enemyQuantity) {
            case 1:
                if(enemyArray[ENEMY1].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[0].getElementalAttribute(), enemyArray[0].getPhysicalAttack(), enemyArray[0].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY1].setVisible(false);
                }
                break;
            case 2:
                if (enemyArray[ENEMY1].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[0].getElementalAttribute(), enemyArray[0].getPhysicalAttack(), enemyArray[0].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY1].setVisible(false);
                }
                if (enemyArray[ENEMY2].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[1].getElementalAttribute(), enemyArray[1].getPhysicalAttack(), enemyArray[1].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY2].setVisible(false);
                }
                break;
            case 3:
                if (enemyArray[ENEMY1].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[0].getElementalAttribute(), enemyArray[0].getPhysicalAttack(), enemyArray[0].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY1].setVisible(false);
                }
                if (enemyArray[ENEMY2].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[1].getElementalAttribute(), enemyArray[1].getPhysicalAttack(), enemyArray[1].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY2].setVisible(false);
                }
                if (enemyArray[ENEMY3].isAlive()) {
                    currentPlayer.calculateDamage(enemyArray[2].getElementalAttribute(), enemyArray[2].getPhysicalAttack(), enemyArray[2].getCurrentLevel());
                    this.isAttacking = true;
                } else {
                    enemyArray[ENEMY3].setVisible(false);
                }
                break;
            default:
                System.out.println("Bad attack perform.");
        }
        this.status = BattleManager.ACTIVE_BATTLE;
        this.currentAttacker = ENEMY1;
        this.isAttacking = true;
        this.movingForward = true;
    }
    //Cross-Item selection---------------------------
    private void useHealthPotion() {
        if (healthPotions > 0) {
            switch (currentTarget) {
                case PLAYER:
                    currentPlayer.setCurrentHealthPoints(currentPlayer.getMaxHealthPoints() / 3);
                    break;
                case ENEMY1:
                    enemyArray[ENEMY1].setCurrentHealthPoints(enemyArray[ENEMY1].getMaxHealthPoints() / 3);
                    break;
                case ENEMY2:
                    enemyArray[ENEMY2].setCurrentHealthPoints(enemyArray[ENEMY2].getMaxHealthPoints() / 3);
                    break;
                case ENEMY3:
                    enemyArray[ENEMY3].setCurrentHealthPoints(enemyArray[ENEMY3].getMaxHealthPoints() / 3);
                    break;
                default:
                    System.out.println("Bad target.");
            }
            healthPotions--;
        } else {
            backSelection();
        }

    }

    //use of the ether potion
    private void useEtherPotion() {
        switch (currentTarget) {
            case PLAYER:
                currentPlayer.setCurrentMagicPoints(currentPlayer.getMaxMagicPoints() / 3);
                break;
            case ENEMY1:
                enemyArray[ENEMY1].setCurrentMagicPoints(enemyArray[ENEMY1].getMaxMagicPoints() / 3);
                break;
            case ENEMY2:
                enemyArray[ENEMY2].setCurrentMagicPoints(enemyArray[ENEMY2].getMaxMagicPoints() / 3);
                break;
            case ENEMY3:
                enemyArray[ENEMY3].setCurrentMagicPoints(enemyArray[ENEMY3].getMaxMagicPoints() / 3);
                break;
            default:

        }
    }

    //Use of the promo potion
    private void usePromoPotion() {
        switch (currentTarget) {
            case PLAYER:
                currentPlayer.setCurrentHealthPoints(currentPlayer.getMaxHealthPoints());
                currentPlayer.setCurrentMagicPoints(currentPlayer.getMaxMagicPoints());
                break;
            case ENEMY1:
                enemyArray[ENEMY1].setCurrentHealthPoints(enemyArray[ENEMY1].getMaxHealthPoints());
                enemyArray[ENEMY1].setCurrentMagicPoints(enemyArray[ENEMY1].getMaxMagicPoints());
                break;
            case ENEMY2:
                enemyArray[ENEMY2].setCurrentHealthPoints(enemyArray[ENEMY2].getMaxHealthPoints());
                enemyArray[ENEMY2].setCurrentMagicPoints(enemyArray[ENEMY2].getMaxMagicPoints());
                break;
            case ENEMY3:
                enemyArray[ENEMY3].setCurrentHealthPoints(enemyArray[ENEMY3].getMaxHealthPoints());
                enemyArray[ENEMY3].setCurrentMagicPoints(enemyArray[ENEMY3].getMaxMagicPoints());
                break;
            default:

        }
    }
    //Cross-Magic selection--------------------------

    //Use of the fire magic
    private void useFireMagic() {
        switch (currentTarget) {
            case PLAYER:
                currentPlayer.calculateDamage(OverworldCharacter.FIRE, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY1:
                enemyArray[ENEMY1].calculateDamage(OverworldCharacter.FIRE, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY2:
                enemyArray[ENEMY2].calculateDamage(OverworldCharacter.FIRE, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY3:
                enemyArray[ENEMY3].calculateDamage(OverworldCharacter.FIRE, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            default:

        }
        currentPlayer.setCurrentMagicPoints(-20);
    }

    //Use of the ice magic
    private void useIceMagic() {
        switch (currentTarget) {
            case PLAYER:
                currentPlayer.calculateDamage(OverworldCharacter.WATER, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY1:
                enemyArray[ENEMY1].calculateDamage(OverworldCharacter.WATER, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY2:
                enemyArray[ENEMY2].calculateDamage(OverworldCharacter.WATER, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            case ENEMY3:
                enemyArray[ENEMY3].calculateDamage(OverworldCharacter.WATER, currentPlayer.getMagicalAttack(), currentPlayer.getCurrentLevel());
                break;
            default:
        }
        currentPlayer.setCurrentMagicPoints(-20);
    }

    //Use of the heal magic
    private void useHealMagic() {
        switch (currentTarget) {
            case PLAYER:
                healEffect.setPosition(currentPlayer.getX() + (currentPlayer.getHeight()-healEffect.getHeight()), currentPlayer.getX());
                currentPlayer.setCurrentHealthPoints(currentPlayer.getMaxHealthPoints());
                break;
            case ENEMY1:
                enemyArray[ENEMY1].setCurrentHealthPoints(enemyArray[ENEMY1].getMaxHealthPoints());
                break;
            case ENEMY2:
                enemyArray[ENEMY2].setCurrentHealthPoints(enemyArray[ENEMY2].getMaxHealthPoints());
                break;
            case ENEMY3:
                enemyArray[ENEMY3].setCurrentHealthPoints(enemyArray[ENEMY3].getMaxHealthPoints());
                break;
            default:

        }
        currentPlayer.setCurrentMagicPoints(-30);
    }
    //Cross-Attack selection-------------------------

    //Use of a physical attack
    private void usePhysicalAttack() {
        System.out.println("Current Target: " + currentTarget);
        switch (currentTarget) {
            case PLAYER:
                currentPlayer.calculateDamage(mainPlayer.getElementalAttribute(), mainPlayer.getPhysicalAttack(), mainPlayer.getCurrentLevel());
                break;
            case ENEMY1:
                enemyArray[ENEMY1].calculateDamage(mainPlayer.getElementalAttribute(), mainPlayer.getPhysicalAttack(), mainPlayer.getCurrentLevel());
                System.out.println("Enemy HP: " + enemyArray[ENEMY1].getCurrentHealthPoints()); //Testing only
                break;
            case ENEMY2:
                enemyArray[ENEMY2].calculateDamage(mainPlayer.getElementalAttribute(), mainPlayer.getPhysicalAttack(), mainPlayer.getCurrentLevel());
                break;
            case ENEMY3:
                enemyArray[ENEMY3].calculateDamage(mainPlayer.getElementalAttribute(), mainPlayer.getPhysicalAttack(), mainPlayer.getCurrentLevel());
                break;
            default:

        }
    }
    //Cross-Escape selection-------------------------
    /**
     * Calculates the escape probability and checks if player can run away. If not, the enemy
     * attacks and adds one to a counter. If the counter is 3, the player can escape safely.
     */
    public void escape() {
        double escapeProbability = this.generateRandomNumber();
        System.out.println("Escape probability: " + escapeProbability);
        if (escapeCounter < 2) {
            if (escapeProbability > 0.70) {
                screen.finishBattle();
            } else {
                escapeCounter++;
                status = ACTIVE_ENEMY_TURN;
            }
        } else {
            screen.finishBattle();
        }
    }
    //<<------------------------------------------------------------------------------------------------------------------------------------>>
}
